import { Injectable, ConflictException, BadRequestException, NotFoundException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { SendCodeDto } from './dto/send-code.dto';
import { RegisterDto } from './dto/register.dto';
import { LoginWithPasswordDto } from './dto/login-with-password.dto';
import { LoginWithCodeDto } from './dto/login-with-code.dto';

import { User } from './entities/user.entity';
import * as bcrypt from 'bcrypt';

/**
 * 验证码存储结构
 * @property code - 验证码字符串
 * @property expiresAt - 过期时间戳(ms)
 * @property lastSentAt - 最后发送时间戳(ms)
 */
// 模拟验证码存储（实际项目中建议使用Redis）
const codeStore = new Map<string, { code: string; expiresAt: number; lastSentAt: number }>();

/**
 * 认证服务类
 * 处理用户登录、验证码发送和JWT生成等认证相关业务逻辑
 */
@Injectable()
export class AuthService {
  /**
   * 构造函数
   * @param jwtService - JWT服务对象
   * @param configService - 配置服务对象
   * @param userRepository - 用户仓储对象
   */
  constructor(
    private jwtService: JwtService,
    private configService: ConfigService,
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}

  /**
   * 发送验证码
   * @param sendCodeDto - 包含手机号的DTO
   * @returns 发送状态和消息
   */
  sendCode(sendCodeDto: SendCodeDto): { success: boolean; message: string } {
    const { phone } = sendCodeDto;
    
    // 检查频率限制（一分钟内只能发送一次）
    const storedData = codeStore.get(phone);
    if (storedData && Date.now() - storedData.lastSentAt < 60000) {
      return { success: false, message: '一分钟内只能发送一次验证码' };
    }

    // 生成6位数字验证码
    const code = Math.floor(100000 + Math.random() * 900000).toString();
    // 验证码有效期5分钟
    const expiresAt = Date.now() + 5 * 60 * 1000;
    const lastSentAt = Date.now();

    // 存储验证码和发送时间
    codeStore.set(phone, { code, expiresAt, lastSentAt });

    // TODO: 集成实际短信服务API发送验证码
    console.log(`向手机号 ${phone} 发送验证码: ${code}`);

    return { success: true, message: '验证码已发送' };
  }

  /**
   * 用户注册
   * @param registerDto - 包含手机号、密码和验证码的DTO
   * @returns 包含token和用户信息的对象
   * @throws UnauthorizedException - 验证码无效时抛出
   * @throws ConflictException - 用户已存在时抛出
   */
  async register(registerDto: RegisterDto): Promise<{ token: string; user: Omit<User, 'password'> }> {
    const { phone, code, password } = registerDto;
    // 验证验证码
    const storedCode = codeStore.get(phone);

    if (!storedCode || storedCode.code !== code || storedCode.expiresAt < Date.now()) {
      throw new BadRequestException('验证码无效或已过期');
    }

    // 验证成功后删除验证码
    codeStore.delete(phone);

    // 检查用户是否已存在
    const existingUser = await this.userRepository.findOneBy({ phone });
    if (existingUser) {
      throw new ConflictException('该手机号已注册，请直接登录');
    }

    // 创建新用户，加密密码
    const hashedPassword = await bcrypt.hash(password, 10);
    const user = this.userRepository.create({
      phone,
      username: `用户${phone.slice(-4)}`,
      password: hashedPassword,
    });
    await this.userRepository.save(user);

    // 生成JWT
    const token = this.generateToken(user);

    // 返回token和用户信息（不含密码）
    const { password: _, ...userWithoutPassword } = user;
    return { token, user: userWithoutPassword };
  }

  /**
   * 手机号密码登录
   * 验证手机号和密码是否匹配，成功后返回token
   * @param loginWithPasswordDto - 包含手机号和密码的DTO
   * @returns 包含token和用户信息的对象
   * @throws UnauthorizedException - 手机号不存在或密码错误时抛出
   */
  async loginWithPassword(loginWithPasswordDto: LoginWithPasswordDto): Promise<{ token: string; user: Omit<User, 'password'> }> {
    const { phone, password } = loginWithPasswordDto;

    // 查找用户
    const user = await this.userRepository.findOneBy({ phone });
    if (!user) {
      throw new NotFoundException('该手机号未注册，请先注册');
    }

    // 验证密码
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      throw new BadRequestException('密码错误');
    }

    // 生成JWT
    const token = this.generateToken(user);

    // 返回token和用户信息（不含密码）
    const { password: _, ...userWithoutPassword } = user;
    return { token, user: userWithoutPassword };
  }

  /**
   * 手机号验证码登录
   * 验证手机号和验证码，成功后返回token
   * @param loginWithCodeDto - 包含手机号和验证码的DTO
   * @returns 包含token和用户信息的对象
   * @throws UnauthorizedException - 验证码无效或用户不存在时抛出
   */
  async loginWithCode(loginWithCodeDto: LoginWithCodeDto): Promise<{ token: string; user: Omit<User, 'password'> }> {
    const { phone, code } = loginWithCodeDto;

    // 查找用户
    const user = await this.userRepository.findOneBy({ phone });
    if (!user) {
      throw new NotFoundException('用户不存在，请先注册');
    }

    // 验证验证码
    const storedCode = codeStore.get(phone);

    if (!storedCode || storedCode.code !== code || storedCode.expiresAt < Date.now()) {
      throw new BadRequestException('验证码无效或已过期');
    }

    // 验证成功后删除验证码
    codeStore.delete(phone);

    // 生成JWT
    const token = this.generateToken(user);

    // 返回token和用户信息（不含密码）
    const { password: _, ...userWithoutPassword } = user;
    return { token, user: userWithoutPassword };
  }

  /**
   * 根据ID获取用户信息
   * @param id - 用户ID
   * @returns 用户信息或null
   */
  async getUserById(id: string): Promise<User | null> {
    return this.userRepository.findOneBy({ id: Number(id) });
  }

  /**
   * 生成JWT令牌
   * @param user - 用户实体对象
   * @returns JWT令牌字符串
   */
  private generateToken(user: User): string {
    const payload = {
      sub: user.id,
      phone: user.phone,
    };

    return this.jwtService.sign(payload, {
      secret: this.configService.get('JWT_SECRET'),
      expiresIn: this.configService.get('JWT_EXPIRES_IN') || '1d',
    });
  }
}
